---
title: 'Azure Active Directory Authentication: Cypress Guide'
sidebar_label: Azure Active Directory Authentication
description: Learn how to set up Cypress to test against an Azure Active Directory web app and authenticate with Azure Active Directory using `cy.origin()`.
---

<ProductHeading product="app" />

# Azure Active Directory Authentication

:::info

##### <Icon name="question-circle" color="#4BBFD2" /> What you'll learn

- How to set up Cypress to test against an Azure Active Directory web app
- How to authenticate with Azure Active Directory using `cy.origin()`

:::

This guide is designed for testing against a Single Page Application (SPA) that
uses
[Azure Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-whatis)
(AAD) to authenticate users. For this guide, the Microsoft Authentication
Library
[`@azure/msal-browser`](https://www.npmjs.com/package/@azure/msal-browser)
package is used by the web app to broker this authentication.

This guide can also serve as a foundation for testing other web apps with
Cypress that use Azure Active Directory services with other frameworks, such as
React, Angular, or Vue.

## Microsoft AAD Application Setup

For this guide, we are mainly going to focus on setting up Cypress to test
against an Azure Active Directory web app. Please clone the
[Microsoft Identity JavaScript Tutorial](https://github.com/Azure-Samples/ms-identity-javascript-tutorial/tree/c1956b658efa331bb5df11a0038ad32d12dad3ce/1-Authentication/1-sign-in)
and follow the steps to set up your application.

Once set up, you'll need to modify a few things in the `App/index.html` file:

- Remove any
  [`integrity`](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
  attributes inside `<script>` tags. This is to prevent any SRI attribute
  mismatches, which can happen since Cypress must rewrite any
  [framebusting code](/app/references/configuration#modifyObstructiveCode).
  This is recommended only when an application is under test.
- Uncomment the `authRedirect.js` `<script>` tag and comment out the
  `authPopup.js` `<script>` tag. Authentication
  [Pop ups](/app/references/trade-offs#Permanent-trade-offs) will not work
  inside of Cypress.

Inside `server.js`, you'll see a reference to `express-rate-limit`, which
limits each window to 100 requests per 15 minutes. Under test, your application
will likely exceed this as you will be reloading your application and making
requests to broker/verify authentication. For this demo, you can either

- Remove the rate limiting code (recommended for demo purposes only).
- Implement a strategy to increase the rate limit under test.

When finished, your SPA should be running on `http://localhost:3000` and be
properly configured to run with Azure Active Directory and Cypress.

## Configuring Cypress to use Microsoft AAD

To have access to test user credentials within our tests, we need to configure
Cypress to use the
[Azure Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-whatis)
user credentials for a given user in your tenant. These user credentials should
be set in the `cypress.env.json` file inside the root directory of your project.

```json title="cypress.env.json"
{
  "aad_username": "AAD_USERNAME",
  "aad_password": "AAD_PASSWORD",
  "aad_name": "AAD_NAME"
}
```

Also, to authenticate with Azure Active Directory, you'll need to enable the
[`experimentalModifyObstructiveThirdPartyCode`](/app/guides/cross-origin-testing#Modifying-Obstructive-Third-Party-Code)
configuration option in the e2e configuration. If not enabled, your
authentication workflow will enter an infinite redirect loop.

:::cypress-config-example

```js
{
  e2e: {
    experimentalModifyObstructiveThirdPartyCode: true
  }
}
```

:::

## Login with [`cy.origin()`](/api/commands/origin)

Next, we'll write a custom command called `loginToAAD` to perform a login to
[Azure Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-whatis).
This command will use [`cy.origin()`](/api/commands/origin) to

1. Navigate to the Azure Active Directory login page on
   `login.microsoftonline.com`
2. Input user credentials
3. Sign in and redirect back to the demo application
4. Cache the results with [`cy.session()`](/api/commands/session)

```js title="cypress/support/commands.ts"
function loginViaAAD(username: string, password: string) {
  cy.visit('http://localhost:3000/')
  cy.get('button#signIn').click()

  // Login to your AAD tenant.
  cy.origin(
    'login.microsoftonline.com',
    {
      args: {
        username,
      },
    },
    ({ username }) => {
      cy.get('input[type="email"]').type(username, {
        log: false,
      })
      cy.get('input[type="submit"]').click()
    }
  )

  // depending on the user and how they are registered with Microsoft, the origin may go to live.com
  cy.origin(
    'login.live.com',
    {
      args: {
        password,
      },
    },
    ({ password }) => {
      cy.get('input[type="password"]').type(password, {
        log: false,
      })
      cy.get('input[type="submit"]').click()
      cy.get('#idBtn_Back').click()
    }
  )

  // Ensure Microsoft has redirected us back to the sample app with our logged in user.
  cy.url().should('equal', 'http://localhost:3000/')
  cy.get('#welcome-div').should(
    'contain',
    `Welcome ${Cypress.env('aad_username')}!`
  )
}

Cypress.Commands.add('loginToAAD', (username: string, password: string) => {
  const log = Cypress.log({
    displayName: 'Azure Active Directory Login',
    message: [`🔐 Authenticating | ${username}`],
    autoEnd: false,
  })
  log.snapshot('before')

  loginViaAAD(username, password)

  log.snapshot('after')
  log.end()
})
```

Now, we can use our `loginToAAD` command in the test. Below is our test to login
as a user via Azure Active Directory and run a basic sanity check.

```js title="auth.cy.js"
describe('Azure Active Directory Authentication', () => {
  beforeEach(() => {
    // log into Azure Active Directory through our sample SPA using our custom command
    cy.loginToAAD(Cypress.env('aad_username'), Cypress.env('aad_password'))
    cy.visit('http://localhost:3000')
  })

  it('verifies the user logged in has the correct name', () => {
    cy.get('#table-body-div td:contains("name") + td').should(
      'contain',
      `${Cypress.env('aad_name')}`
    )
  })

  it('verifies the user logged in has the correct preferred name', () => {
    cy.get('#table-body-div td:contains("preferred_username") + td').should(
      'contain',
      `${Cypress.env('aad_username')}`
    )
  })
})
```

<DocsVideo
  src="https://vimeo.com/789095126"
  title="Azure Active Directory authentication with cy.origin()"
/>

We now have working authentication and tests! But we are logging in before every
test, which is not only time consuming, but also can lead to API rate limiting
due to the number of requests.

For this, we can refactor our login command to take advantage of
[`cy.session()`](/api/commands/session) to store our logged in user's tokens
and/or cookies so we don't have to reauthenticate before every test.

```ts title="cypress/support/commands.ts"
Cypress.Commands.add('loginToAAD', (username: string, password: string) => {
  cy.session(
    `aad-${username}`,
    () => {
      const log = Cypress.log({
        displayName: 'Azure Active Directory Login',
        message: [`🔐 Authenticating | ${username}`],
        // @ts-ignore
        autoEnd: false,
      })

      log.snapshot('before')

      loginViaAAD(username, password)

      log.snapshot('after')
      log.end()
    },
    {
      validate: () => {
        // this is a very basic form of session validation for this demo.
        // depending on your needs, something more verbose might be needed
        cy.visit('http://localhost:3000')
        cy.get('#welcome-div').should(
          'contain',
          `Welcome ${Cypress.env('aad_username')}!`
        )
      },
    }
  )
})
```

<DocsVideo
  src="https://vimeo.com/789095038"
  title="Azure Active Directory authentication with cy.session()"
/>

With the use of [`cy.session()`](/api/commands/session), our tests should now
run quicker!

We hope this guide was able to get you up and running with
[`cy.origin()`](/api/commands/origin) and
[`cy.session()`](/api/commands/session). If you ran into any issues while
following this guide, or have any feedback, please let us know and submit a
[GitHub issue](https://github.com/cypress-io/cypress-documentation/issues/new/choose).
Happy testing!

## See also

- [Cross Origin Testing Guide](/app/guides/cross-origin-testing)
- [AWS Cognito Authentication Guide](/app/guides/authentication-testing/amazon-cognito-authentication)
- [Okta Authentication Guide](/app/guides/authentication-testing/okta-authentication)
- [`cy.origin()`](/api/commands/origin)
- [`cy.session()`](/api/commands/session)
